package com.iteaj.iot.codec;

import com.iteaj.iot.SocketMessage;
import com.iteaj.iot.ProtocolException;
import com.iteaj.iot.message.UnParseBodyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCounted;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;

/**
 * create time: 2021/2/21
 *  基于套接字的解码器
 * @author iteaj
 * @since 1.0
 */
public interface SocketMessageDecoder<M extends SocketMessage, R extends ReferenceCounted> {

    /**
     *
     * 对decode方法进行代理
     * @see #decode(ChannelHandlerContext, R)
     * @param ctx
     * @param in
     * @return
     */
    default M proxy(ChannelHandlerContext ctx, R in) throws Exception {
        M message = decode(ctx, in);
        if(message instanceof UnParseBodyMessage) {
            message = (M) ((UnParseBodyMessage) message).build();
        }

        return message;
    }

    /**
     * 解码单条报文
     * @param ctx
     * @param in
     * @return
     * @throws Exception
     */
    default M decode(ChannelHandlerContext ctx, R in) throws Exception {
        if(in instanceof ByteBuf) {
            final int readableBytes = ((ByteBuf) in).readableBytes();
            byte[] message = new byte[readableBytes];
            ((ByteBuf) in).readBytes(message);
            return createMessage(message);
        } else {
            throw new IllegalStateException("不支持的报文["+in.getClass().getSimpleName()+"]");
        }
    }

    /**
     * 创建报文, 默认使用带有参数{@link byte[]}的构造函数创建
     * @since 2.3.0 方便使用者覆写此方法
     * @param message
     * @return
     */
    default M createMessage(byte[] message) {
        try {
            Constructor<M> constructor = getMessageClass().getConstructor(byte[].class);
            return BeanUtils.instantiateClass(constructor, message);
        } catch (Exception e) {
            throw new ProtocolException("找不到构造函数["+getMessageClass().getSimpleName()+"(byte[].class)], 请增加对应的构造函数或者自定义解码", e.getCause());
        }
    }

    Class<M> getMessageClass();

    /**
     * 解码报文列表
     * @param ctx
     * @param in
     * @return
     * @throws Exception
     */
    default List<M> decodes(ChannelHandlerContext ctx, R in) throws Exception {
        M decode = proxy(ctx, in);
        if(decode != null) {
            return Arrays.asList(decode);
        } else {
            return null;
        }
    }
}
